package com.cqs.learning.spring.cloud.producer.service;

import com.cqs.learning.spring.cloud.producer.event.ConfigEvent;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 模拟nacos作为远程配置中心得处理逻辑
 * <p>
 * 客户段和服务段使用长轮询
 * (nacos server)服务端，异步处理请求，并延迟29.5秒返回
 * <p>
 * 若中途有修改配置，借助监听机制，立刻返回结果确保实时性
 * 此时要注意是同一个Async Context
 *
 * @author lixw
 * @date created in 17:28 2020/5/4
 */
@Service
@Log4j2
public class ConfigService {


    /**
     * 远程配置
     */
    private volatile Integer config;
    final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void asyncReadConfig(final AsyncContext asyncContext) {
        // AsyncContext.setTimeout()的超时时间不准，所以只能自己控制
        asyncContext.setTimeout(0L);
        int delay = 29;
        //延迟delay秒执行
        ReadConfigTask readConfigTask = new ReadConfigTask(asyncContext);
        tasks.add(readConfigTask);
        scheduler.schedule(readConfigTask, delay, TimeUnit.SECONDS);
    }

    /**
     * 修改配置文件 并发布事件(event)
     *
     * @return
     */
    public String pushEvent() {
        config = new Random().nextInt(10000);
        log.info("修改远程配置config:{}", config);
        List<ReadConfigTask> list = null;
        synchronized (tasks) {
            if (tasks.size() > 0) {
                list = new ArrayList<>(tasks);
            }
            tasks.clear();
        }
        if (list != null) {
            list.stream().forEach(it -> {
                ConfigEvent configEvent = new ConfigEvent(it);
                tasks.remove(it);
                log.info("发布修改配置事件....." + configEvent.getSource());
                applicationEventPublisher.publishEvent(configEvent);
            });

        }
        return "config:" + config;
    }

    class ReadConfigTask implements Runnable {
        private final AsyncContext asyncContext;

        ReadConfigTask(AsyncContext context) {
            this.asyncContext = context;
        }

        @Override
        public void run() {
            boolean exec = false;
            synchronized (tasks) {
                //组合操作
                if (exec = tasks.contains(this)) {
                    tasks.remove(this);
                }
            }
            if (exec) {
                generateResponse();
            }

        }

        void generateResponse() {
            HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
            try {
                String respString = String.valueOf("config = " + config);
                // 禁用缓存
                response.setHeader("Pragma", "no-cache");
                response.setDateHeader("Expires", 0);
                response.setHeader("Cache-Control", "no-cache,no-store");
                response.setStatus(HttpServletResponse.SC_OK);
                response.getWriter().println(respString);
                log.info("asyncContext:" + asyncContext);
                asyncContext.complete();
            } catch (Exception se) {
                asyncContext.complete();
            }
        }
    }


    private final Set<ReadConfigTask> tasks = new CopyOnWriteArraySet<>();
}
